Visual IRC 2.0 Automation Interface Jesse McGrew August 30, 2003 Interface primer ================ The COM standard defines a way to access objects by their interfaces. An interface is basically a list of the object's capabilities (properties and methods). Objects can support several interfaces at once, and if a program has a pointer to one of an object's interfaces, it can query the object to see if it supports another interface that the program wants to use. An interface defines how you can interact with an object, but not how the object works internally. Different programs can use different languages to create objects, but if they all implement the same interface, they can be used exactly the same way. There are various ways of getting a pointer to one of the interfaces supplied by an object. Once a pointer is retrieved, a compiled programs can access the interface methods directly, as long as a type library is available when the program is written. However, when the description of the interface isn't available at compile time, or if the program is written in an uncompiled language (e.g. a ViRC script), there's another way to access the interface methods. OLE Automation defines an interface called IDispatch, which is used to access methods and properties by name. Most interfaces that applications expose to the public are 'dual interfaces', meaning they can be accessed directly and also through IDispatch. Using OLE Automation interfaces from ViRC scripts ================================================= OVS handles can now refer to OLE Automation interfaces (IDispatch) obtained from other programs, as well as objects from the VCL and user-defined classes. The predefined handle "1" can be used to refer to ViRC's IVisualIRC interface (see below), similar to how "0" can be used to refer to ViRC's main window. OVS handles that refer to OLE Automation interfaces work almost just like other handles. The differences are: 1. To create a handle referring to an Automation object, use $NewCOMObject() instead of $New(). This function takes a CLSID surrounded by curly brackets. For example, to create a new ViRC controller object that implements the IVisualIRC interface, use: $NewCOMObject({5F0D2E3B-66D5-4E11-B9D2-1756A53D4EDB}). The return value is an object handle, or -1 if the class doesn't exist or doesn't support IDispatch. 2. $ClassOf() always returns "IDispatch" when used with an OLE handle. $ParentClassOf() always returns "IUnknown". 3. Properties of properties (or methods of properties) cannot be accessed directly, as in $obj.Prop.Method. You must map the property, then call the method with the new handle. 4. When calling a method through an Automation handle, each parameter is treated as a list item: $obj.Method param1 param2 $ListQuote(param3 contains spaces) 5. Indexed properties can be used with $Prop(), $MapProp(), and @P by giving the index parameters in square brackets after the property name. Put a space before the brackets so ViRC doesn't parse it as an array reference. Just like in a method call, each index must be quoted as a list item when appropriate: $Prop($obj.IndexedProp [index1 "index 2 contains spaces"]) 6. Destroy, SafeDestroy, and UnmapObject all have the same effect on Automation handles. The object won't actually be destroyed until all of its interface pointers are released, including any pointers held by other programs. Other programs can map their own Automation objects into ViRC by calling the IVisualIRC::MapInterface method. Here's an example in Delphi: const CLSID_VisualIRC: TGUID = '{5F0D2E3B-66D5-4E11-B9D2-1756A53D4EDB}'; LocalContext = CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER; var MyObject: IMyInterface; // must be a dual interface VisualIRC: IVisualIRC; Handle: Integer; begin // create your object and get its interface pointer MyObject := TMyObject.Create as IMyInterface; // get an IVisualIRC pointer OleCheck(CoCreateInstance(CLSID_VisualIRC, nil, LocalContext, IDispatch, VisualIRC)); // map the interface pointer to a handle Handle := VisualIRC.MapInterface(MyObject as IDispatch); end; Exposing objects from ViRC scripts to other programs ==================================================== Interfaces exposed by ViRC ========================== Better documentation may be provided later. For now, here is a listing of the dispatch interfaces implemented by ViRC. Note that an IVisualIRC interface is provided to all Language..EndLanguage blocks under the name 'VisualIRC', an IVSSprout is provided under the names 'ViRCScript' and 'Versus', and an interface to the current window is provided under the name 'Current' (the interface is an IServer, IQuery, IChannel, IDCCChat, or IWhiteboard). The 'Form' property returns an IDispatch interface that can be used to access the properties of VCL objects. For example, this alias using VBScript will change the caption of the window it is executed from: Alias SETCAPTION Language VBScript Current.Form.Caption = Versus.GetVar("$1-") EndLanguage EndAlias Child controls can be used as properties as well: Alias DISABLETRAY Language VBScript ' properties of "VisualIRC" can be used as global names (e.g. MainForm) MainForm.TrayIcon.Active = False EndLanguage EndAlias VCL objects can only be used through their IDispatch interfaces; all other ViRC objects implement dual interfaces so they can use vtable binding with the type library from ViRC.exe. Each vtable interface has the same GUID as its respective dispatch interface. The GetInterface method of IVSSprout can be used to retrieve an IDispatch interface for an OVS object, given the object's handle. VCL classes and user-defined classes can be accessed this way. For example: Language VBScript Set foo = VisualIRC.GlobalInterpreter.GetInterface(0) foo.Caption = "Here is a new main form caption" EndLanguage Other applications can obtain an IVisualIRC interface through the CoClass with CLSID = {5F0D2E3B-66D5-4E11-B9D2-1756A53D4EDB}. Implemented dispatch interfaces: IVisualIRC = dispinterface ['{A2F519D5-32EB-484A-BC70-CA25F4DB7080}'] property Version: Integer readonly dispid 2; property MainForm: IDispatch readonly dispid 3; property GlobalInterpreter: IVSSprout readonly dispid 4; property ServerCount: Integer readonly dispid 6; property Servers[Index: Integer]: IServer readonly dispid 7; property DCCChatCount: Integer readonly dispid 8; property DCCChats[Index: Integer]: IDCCChat readonly dispid 9; function NewServer(const ServerName: WideString): IServer; dispid 10; end; IChildForm = dispinterface ['{B14CAFEE-6E91-42FF-9D15-3937D03D3923}'] property Kind: WindowKind readonly dispid 1; property ID: Integer readonly dispid 2; property Owner: IChildForm readonly dispid 3; procedure Close; dispid 4; property WindowName: WideString readonly dispid 5; property Form: IDispatch readonly dispid 6; end; IOutputForm = dispinterface // inherits from IChildForm ['{CC885F1F-A1FC-46B8-A722-42E1832201F8}'] procedure Clear(ClearHistory: WordBool); dispid 8; property History: IStrings readonly dispid 9; procedure RunCommand(const Command: WideString); dispid 10; property Output: ITextScroller readonly dispid 11; property Kind: WindowKind readonly dispid 1; property ID: Integer readonly dispid 2; property Owner: IChildForm readonly dispid 3; procedure Close; dispid 4; property WindowName: WideString readonly dispid 5; property Form: IDispatch readonly dispid 6; end; IQuery = dispinterface // inherits from IOutputForm ['{D80D1784-BD7D-46EC-A11D-0D15FB0AB4A1}'] property Nick: WideString readonly dispid 16; procedure Clear(ClearHistory: WordBool); dispid 8; property History: IStrings readonly dispid 9; procedure RunCommand(const Command: WideString); dispid 10; property Output: ITextScroller readonly dispid 11; property Kind: WindowKind readonly dispid 1; property ID: Integer readonly dispid 2; property Owner: IChildForm readonly dispid 3; procedure Close; dispid 4; property WindowName: WideString readonly dispid 5; property Form: IDispatch readonly dispid 6; end; IDCCChat = dispinterface // inherits from IOutputForm ['{A94DC4FF-19E6-470B-B85A-B60987D19A5F}'] property Nick: WideString readonly dispid 16; property Session: IDCCSession readonly dispid 17; procedure Clear(ClearHistory: WordBool); dispid 8; property History: IStrings readonly dispid 9; procedure RunCommand(const Command: WideString); dispid 10; property Output: ITextScroller readonly dispid 11; property Kind: WindowKind readonly dispid 1; property ID: Integer readonly dispid 2; property Owner: IChildForm readonly dispid 3; procedure Close; dispid 4; property WindowName: WideString readonly dispid 5; property Form: IDispatch readonly dispid 6; end; IWhiteboard = dispinterface // inherits from IChildForm ['{25B2DF1C-3683-4B13-8204-A0C37AF36164}'] property Kind: WindowKind readonly dispid 1; property ID: Integer readonly dispid 2; property Owner: IChildForm readonly dispid 3; procedure Close; dispid 4; property WindowName: WideString readonly dispid 5; property Form: IDispatch readonly dispid 6; end; IDCCSession = dispinterface ['{20BCB67E-C1D0-4486-B6CE-4D01A8ABF44F}'] property Window: IChildForm readonly dispid 1; end; IServer = dispinterface // inherits from IOutputForm ['{2F8DFB47-DB99-40F7-B5A6-233BA930ED34}'] property ChannelCount: Integer readonly dispid 16; property Channels[Index: Integer]: IChannel readonly dispid 17; procedure SendLine(const Text: WideString); dispid 18; property Hostname: WideString dispid 19; property ServerName: WideString readonly dispid 20; property Port: Integer readonly dispid 21; procedure Connect; dispid 22; procedure Disconnect; dispid 23; property Interpreter: VSSprout readonly dispid 24; property QueryCount: Integer readonly dispid 25; property Queries[Index: Integer]: IQuery readonly dispid 26; property Socket: ISockets readonly dispid 27; procedure Clear(ClearHistory: WordBool); dispid 8; property History: IStrings readonly dispid 9; procedure RunCommand(const Command: WideString); dispid 10; property Output: ITextScroller readonly dispid 11; property Kind: WindowKind readonly dispid 1; property ID: Integer readonly dispid 2; property Owner: IChildForm readonly dispid 3; procedure Close; dispid 4; property WindowName: WideString readonly dispid 5; property Form: IDispatch readonly dispid 6; end; IVSSprout = dispinterface ['{2CC78AC1-A944-11D1-B136-F9CE35176930}'] procedure SetVar(const Name: WideString; const Value: WideString); dispid 1; procedure SetVarLocal(const Name: WideString; const Value: WideString); dispid 2; function GetVar(const Name: WideString): WideString; dispid 3; procedure Execute(const Command: WideString); dispid 4; procedure ExecuteNoEval(const Command: WideString); dispid 5; function Parse(const Text: WideString): WideString; dispid 6; procedure IncVar(const Name: WideString; Amount: Integer); dispid 7; property State: Integer readonly dispid 8; function GetInterface(Num: Integer): IDispatch; dispid 9; procedure MapInterface(Num: Integer; const Disp: IDispatch); dispid 10; end; ISockets = dispinterface ['{38980E25-C1AC-4CBE-954B-4E3AE482EFE4}'] property IPAddr: WideString dispid 1; property Port: WideString dispid 2; procedure SendText(const Text: WideString); dispid 3; function ReadText: WideString; dispid 4; procedure SendBuffer(Buffer: {??PSafeArray} OleVariant); dispid 5; // array of byte function ReadBuffer(MaxLength: Integer; out Buffer: {??PSafeArray} OleVariant): Integer; dispid 6; // array of byte procedure Connect; dispid 8; procedure Close; dispid 9; procedure Listen; dispid 10; function ListenRange(Low: Integer; High: Integer): Integer; dispid 11; procedure CancelListen; dispid 12; function GetLocalIPAddr(Socket: Integer): WideString; dispid 13; function GetPeerIPAddr(Socket: Integer): WideString; dispid 14; function GetLocalPort(Socket: Integer): Integer; dispid 15; function GetPeerPort(Socket: Integer): Integer; dispid 16; function PeekText: WideString; dispid 17; property SocketNumber: Integer dispid 19; property MasterSocket: Integer dispid 21; property State: SocketState readonly dispid 22; property MaxSockets: Integer readonly dispid 23; property UseSocks: WordBool dispid 24; property SocksIPAddr: WideString dispid 25; property SocksPort: WideString dispid 26; property SocksUsername: WideString dispid 27; property NonBlocking: WordBool dispid 28; property Timeout: Integer dispid 29; property MaximumReceiveLength: Integer dispid 30; function Accept: Integer; dispid 31; end; ITextScroller = dispinterface ['{4E0C8BCD-7B42-42B9-8E4B-43A235C13D74}'] procedure AddFG(FG: Integer; const Text: WideString); dispid 1; procedure AddFGBitmapFile(FG: Integer; const Bitmap: WideString; const Text: WideString); dispid 2; procedure Clear; dispid 3; procedure PageUp; dispid 4; procedure PageDown; dispid 5; function GetText: WideString; dispid 6; procedure FlushBitmaps; dispid 7; procedure FlushOneBitmap(const FileName: WideString); dispid 8; property Background: Integer dispid 9; property BufferSize: Integer dispid 10; property FontName: WideString dispid 11; property FontSize: Integer dispid 12; property HyperlinkColor: Integer dispid 13; property ScriptLinkColor: Integer dispid 14; property TimeStamps: WordBool dispid 15; property TimeStampFormat: WideString dispid 16; property Logging: WordBool dispid 17; end; IChannel = dispinterface // inherits from IOutputForm ['{D8836D1E-E047-4614-8752-4D922C51DAE4}'] property Name: WideString readonly dispid 16; property Mode: WideString readonly dispid 17; property Topic: WideString readonly dispid 18; property Key: WideString readonly dispid 19; property Limit: Integer readonly dispid 20; property TopicSetter: WideString readonly dispid 21; property TopicTime: Integer readonly dispid 22; property TopicHistory: IStrings readonly dispid 23; procedure Clear(ClearHistory: WordBool); dispid 8; property History: IStrings readonly dispid 9; procedure RunCommand(const Command: WideString); dispid 10; property Output: ITextScroller readonly dispid 11; property Kind: WindowKind readonly dispid 1; property ID: Integer readonly dispid 2; property Owner: IChildForm readonly dispid 3; procedure Close; dispid 4; property WindowName: WideString readonly dispid 5; property Form: IDispatch readonly dispid 6; end; // Constants for enum WindowKind type WindowKind = TOleEnum; const Server = $00000000; CHANNEL = $00000001; QUERY = $00000002; DCCCHAT = $00000003; WHITEBOARD = $00000004; // Constants for enum SocketState type SocketState = TOleEnum; const sockDisconnected = $00000000; sockDNS = $00000001; sockConnecting = $00000002; sockConnected = $00000003; sockRequestedSocks = $00000004;